﻿using System.Drawing;
using System.Runtime.InteropServices;

using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.DirectInput;
using SlimDX.Windows;
using System;
using Device = SlimDX.Direct3D9.Device;
using SwapChain = SlimDX.Direct3D9.SwapChain;
using System.Windows.Forms;

namespace Terrain
{
	class TerrainMain : IDisposable
	{
		RenderForm form;

		Device device;

		Keyboard keyboard;
		Mouse mouse;

		Timer timer;
		QuaternionCamera camera;

		Terrain terrain;
		SimpleMeshObject czolg;

		bool wireframeOn = false;

		public void Run()
		{
			InitializeForm();
			InitializeDirectXDevice();
			InitializeDirectXDeviceDefaults();
			InitializeInput();

			camera = new QuaternionCamera(device);
			camera.Position(new Vector3(-10, 10, -10), Vector3.Zero);
			camera.SetProjection(1, 1000);

			terrain = new Terrain(device, 128);
			terrain.CreateTerrain(1.0f, 3);

			czolg = new SimpleMeshObject(device, 1.0f);

			timer = new Timer();

			MessagePump.Run(form, () =>
			{
				double dt = timer.GetTimeInterval();
				ReadKeyboard(100 * dt);
				ReadMouse();
				camera.SetView();
												
				Render();				
			});
		}
				
		private void Render()
		{
			device.SetRenderState(RenderState.FillMode, wireframeOn ? FillMode.Wireframe : FillMode.Solid);

			device.BeginScene();
			{
				device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);

				
				terrain.Draw();
				//czolg.Render();
				
			}
			device.EndScene();
			device.Present();
		}
		
		#region ReadingInput
		private void ReadKeyboard(double dt)
		{
			KeyboardState keys = keyboard.GetCurrentState();

			if (keys.IsPressed(Key.LeftShift))
				dt *= 8;
			if (keys.IsPressed(Key.Escape))
				form.Close();
			if (keys.IsPressed(Key.W))
				camera.Move(dt);
			if (keys.IsPressed(Key.S))
				camera.Move(-dt);
			if (keys.IsPressed(Key.A))
				camera.Strafe(dt);
			if (keys.IsPressed(Key.D))
				camera.Strafe(-dt);
			if (keys.IsPressed(Key.Q))
				camera.MoveVert(dt);
			if (keys.IsPressed(Key.Z))
				camera.MoveVert(-dt);
			if (keys.IsPressed(Key.F1))
				wireframeOn = true;
			if (keys.IsPressed(Key.F2))
				wireframeOn = false;
			
		}

		public void ReadMouse()
		{
			mouse.Poll();

			MouseState state = mouse.GetCurrentState();
			bool[] buttonBuffer = state.GetButtons();

			if (buttonBuffer[0]) camera.MouseMove(state.X, state.Y);
		}
		#endregion ReadingInput

		#region InitializationMethods
		public void InitializeForm()
		{
			form = new RenderForm("Terrain");
		}

		public void InitializeDirectXDevice()
		{
			device = new Device(new Direct3D(), 0, SlimDX.Direct3D9.DeviceType.Hardware, form.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters()
			{
				SwapEffect = SwapEffect.Discard,
				BackBufferFormat = Format.A8R8G8B8,
				BackBufferWidth = form.ClientSize.Width,
				BackBufferHeight = form.ClientSize.Height,
				AutoDepthStencilFormat = Format.D16,
				EnableAutoDepthStencil = true,
				PresentationInterval = PresentInterval.Immediate,
			});
		}

		public void InitializeDirectXDeviceDefaults()
		{
			device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear);
			device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear);
			device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear);
			device.SetRenderState(RenderState.ZEnable, true);
			device.SetRenderState(RenderState.AlphaBlendEnable, true);
			device.SetRenderState(RenderState.PointSpriteEnable, true);
			device.SetRenderState(RenderState.PointScaleEnable, true);
			device.SetRenderState(RenderState.PointScaleA, 0);
			device.SetRenderState(RenderState.PointScaleB, 0);
			device.SetRenderState(RenderState.PointScaleC, 0.002f);

			Light light = new Light();
			light.Diffuse = Color.White;
			light.Ambient = Color.White;
			light.Direction = Vector3.Normalize(new Vector3(-1, -1, 0));
			light.Position = new Vector3(0, 50, 0);
			light.Type = LightType.Directional;
			device.SetLight(0, light);
			device.EnableLight(0, true);
		}

		public void InitializeInput()
		{
			DirectInput directInput = new DirectInput();

			keyboard = new Keyboard(directInput);
			keyboard.SetCooperativeLevel(form, CooperativeLevel.Nonexclusive | CooperativeLevel.Background);
			keyboard.Acquire();

			mouse = new SlimDX.DirectInput.Mouse(directInput);
			mouse.SetCooperativeLevel(form, CooperativeLevel.Nonexclusive | CooperativeLevel.Background);
			mouse.Properties.AxisMode = DeviceAxisMode.Relative;
			mouse.Acquire();
		}
		#endregion InitializationMethods

		public void Dispose()
		{
			mouse.Dispose();
			keyboard.Dispose();

			foreach (var item in ObjectTable.Objects) item.Dispose();
		}
	}
}